WARNING:
	This is the majority of the stuff that I think will be useful to those intrepid
	souls writing Marathon 2 extensions.   The data contained herein is correct as far
	as I can tell.  I have also included a program that dumps the files out so you can
	see the internal structures, as well as the offsets.  Use this to verify that your
	output is correct.  If you wish to make addendums, please do so at the end of this 
	document and leave this warning intact.  Thanks, and good luck!

-Ryan
rdm4@acpub.duke.edu

/*
	WAD.H
	Thursday, June 30, 1994 10:55:20 PM

	Sunday, July 3, 1994 5:51:47 PM
	I wonder if I should include an element size in the entry_header structure...

	Tuesday, December 13, 1994 4:10:48 PM
	Included element size in the entry_header for the directory size.  The directory
	is application_specific_directory_data_size+sizeof(struct directory_entry).
*/

#define CURRENT_WADFILE_VERSION (2)

#define MAXIMUM_DIRECTORY_ENTRIES_PER_FILE 64
#define MAXIMUM_WADFILE_NAME_LENGTH 64
#define MAXIMUM_UNION_WADFILES 16
#define MAXIMUM_OPEN_WADFILES 3

/* ------------- typedefs */
typedef unsigned long WadDataType;

/* ------------- file structures */
struct wad_header { /* 128 bytes */
	short version;									/* Used internally */
	short data_version;								/* Used by the data.. */
	char file_name[MAXIMUM_WADFILE_NAME_LENGTH];
	unsigned long checksum;
	long directory_offset;
	short wad_count;
	short application_specific_directory_data_size;
	short entry_header_size;
	short directory_entry_base_size;
	unsigned long parent_checksum;	/* If non-zero, this is the checksum of our parent, and we are simply modifications! */
	short unused[20];
};

struct directory_entry { /* 10 bytes */
	long offset_to_start; /* From start of file */
	long length; /* Of total level */
	short index; /* For inplace modification of the wadfile! */
};

struct entry_header { /* 16 bytes */
	WadDataType tag;
	long next_offset; /* From current file location-> ie directory_entry.offset_to_start+next_offset */
	long length; /* Of entry */
	long offset; /* Offset for inplace expansion of data */

	/* Data follows */
};


/*
	TAGS.H
	Sunday, July 3, 1994 5:33:15 PM

	This is a list of all of the tags used by code that uses the wad file format. 
	One tag, KEY_TAG, has special meaning, and KEY_TAG_SIZE must be set to the 
	size of an index entry.  Each wad can only have one index entry.  You can get the
	index entry from a wad, or from all of the wads in the file easily.
	
	Marathon uses the KEY_TAG as the name of the level.
*/

#define MAXIMUM_LEVEL_NAME_SIZE 64

/* OSTypes.. */
#define APPLICATION_CREATOR '52.4'
#define SHAPES_FILE_TYPE 'shp2'
#define SOUNDS_FILE_TYPE 'snd2'
#define FILM_FILE_TYPE 'fil2'

#define SCENARIO_FILE_TYPE 'sce2' /* This is a wad file.. */
#define SAVE_GAME_TYPE 'sga2' /* This is a wad file.. */
#define PHYSICS_FILE_TYPE 'phy2' /* This is a wad file.. */
#define PATCH_FILE_TYPE 'pat2' /* This is a wad file.. */

/* Other tags-  */
#define POINT_TAG 'PNTS'
#define LINE_TAG 'LINS'
#define SIDE_TAG 'SIDS'
#define POLYGON_TAG 'POLY'
#define LIGHTSOURCE_TAG 'LITE'
#define ANNOTATION_TAG 'NOTE'
#define OBJECT_TAG 'OBJS'
#define GUARDPATH_TAG 'pth'
#define MAP_INFO_TAG 'Minf'
#define ITEM_PLACEMENT_STRUCTURE_TAG 'plac'
#define DOOR_EXTRA_DATA_TAG 'door'
#define PLATFORM_STATIC_DATA_TAG 'plat'
#define ENDPOINT_DATA_TAG 'EPNT'
#define MEDIA_TAG 'medi'
#define AMBIENT_SOUND_TAG 'ambi'
#define RANDOM_SOUND_TAG 'bonk'
#define TERMINAL_DATA_TAG 'term'

/* Save/Load game tags. */
#define PLAYER_STRUCTURE_TAG 'plyr'
#define DYNAMIC_STRUCTURE_TAG 'dwol'
#define OBJECT_STRUCTURE_TAG 'mobj'
#define MAP_INDEXES_TAG 'iidx'
#define AUTOMAP_LINES 'alin'
#define AUTOMAP_POLYGONS 'apol'
#define MONSTERS_STRUCTURE_TAG 'mOns'
#define EFFECTS_STRUCTURE_TAG 'fx  '
#define PROJECTILES_STRUCTURE_TAG 'bang'
#define PLATFORM_STRUCTURE_TAG 'PLAT'
#define WEAPON_STATE_TAG 'weap'
#define TERMINAL_STATE_TAG 'cint'

/* Physics model tags */
#define MONSTER_PHYSICS_TAG 'MNpx'
#define EFFECTS_PHYSICS_TAG 'FXpx'
#define PROJECTILE_PHYSICS_TAG 'PRpx'
#define PHYSICS_PHYSICS_TAG 'PXpx'
#define WEAPONS_PHYSICS_TAG 'WPpx'

/*
	Application Specific directory structure...

	This data is copied from the map info data.  It is stored separetely for faster access.
*/
struct directory_data {
	short mission_flags;
	short environment_flags;
	long entry_point_flags;
	char level_name[LEVEL_NAME_LENGTH];
};

enum { /* entry point types- this is per map level (long). */
	_single_player_entry_point= 0x01,
	_multiplayer_cooperative_entry_point= 0x02,
	_multiplayer_carnage_entry_point= 0x04,
	_capture_the_flag_entry_point= 0x08,
	_king_of_hill_entry_point= 0x10
};

enum /* mission flags */
{
	_mission_none= 0x0000,
	_mission_extermination= 0x0001,
	_mission_exploration= 0x0002,
	_mission_retrieval= 0x0004,
	_mission_repair= 0x0008,
	_mission_rescue= 0x0010
};

enum /* environment flags */
{
	_environment_normal= 0x0000,
	_environment_vacuum= 0x0001, // prevents certain weapons from working, player uses oxygen
	_environment_magnetic= 0x0002, // motion sensor works poorly
	_environment_rebellion= 0x0004, // makes clients fight pfhor
	_environment_low_gravity= 0x0008, // low gravity

	_environment_network= 0x2000,	// these two pseudo-environments are used to prevent items 
	_environment_single_player= 0x4000 // from arriving in the items.c code.
};


/* ----------------------------- Map tags ----------------- */
#define POINT_TAG 'PNTS'

An array of:
struct world_point2d {
	short x, y;
};

#define LINE_TAG 'LINS'
/* ------------ line definition */

#define SOLID_LINE_BIT 0x4000
#define TRANSPARENT_LINE_BIT 0x2000
#define LANDSCAPE_LINE_BIT 0x1000
#define ELEVATION_LINE_BIT 0x800
#define VARIABLE_ELEVATION_LINE_BIT 0x400
#define LINE_HAS_TRANSPARENT_SIDE_BIT 0x200

#define SET_LINE_SOLIDITY(l,v) ((v)?((l)->flags|=(word)SOLID_LINE_BIT):((l)->flags&=(word)~SOLID_LINE_BIT))
#define LINE_IS_SOLID(l) ((l)->flags&SOLID_LINE_BIT)

#define SET_LINE_TRANSPARENCY(l,v) ((v)?((l)->flags|=(word)TRANSPARENT_LINE_BIT):((l)->flags&=(word)~TRANSPARENT_LINE_BIT))
#define LINE_IS_TRANSPARENT(l) ((l)->flags&TRANSPARENT_LINE_BIT)

#define SET_LINE_LANDSCAPE_STATUS(l,v) ((v)?((l)->flags|=(word)LANDSCAPE_LINE_BIT):((l)->flags&=(word)~LANDSCAPE_LINE_BIT))
#define LINE_IS_LANDSCAPED(l) ((l)->flags&LANDSCAPE_LINE_BIT)

#define SET_LINE_ELEVATION(l,v) ((v)?((l)->flags|=(word)ELEVATION_LINE_BIT):((l)->flags&=(word)~ELEVATION_LINE_BIT))
#define LINE_IS_ELEVATION(l) ((l)->flags&ELEVATION_LINE_BIT)

#define SET_LINE_VARIABLE_ELEVATION(l,v) ((v)?((l)->flags|=(word)VARIABLE_ELEVATION_LINE_BIT):((l)->flags&=(word)~VARIABLE_ELEVATION_LINE_BIT))
#define LINE_IS_VARIABLE_ELEVATION(l) ((l)->flags&VARIABLE_ELEVATION_LINE_BIT)

#define SET_LINE_HAS_TRANSPARENT_SIDE(l,v) ((v)?((l)->flags|=(word)LINE_HAS_TRANSPARENT_SIDE_BIT):((l)->flags&=(word)~LINE_HAS_TRANSPARENT_SIDE_BIT))
#define LINE_HAS_TRANSPARENT_SIDE(l) ((l)->flags&LINE_HAS_TRANSPARENT_SIDE_BIT)

struct line_data /* 32 bytes */
{
	short endpoint_indexes[2];
	word flags; /* no permutation field */

	world_distance length;
	world_distance highest_adjacent_floor, lowest_adjacent_ceiling;
	
	/* the side definition facing the clockwise polygon which references this side, and the side
		definition facing the counterclockwise polygon (can be NONE) */
	short clockwise_polygon_side_index, counterclockwise_polygon_side_index;
	
	/* a line can be owned by a clockwise polygon, a counterclockwise polygon, or both (but never
		two of the same) (can be NONE) */
	short clockwise_polygon_owner, counterclockwise_polygon_owner;
	
	short unused[6];
};


#define SIDE_TAG 'SIDS'

enum /* side flags */
{
	_control_panel_status= 0x0001,
	_side_is_control_panel= 0x0002,
	_side_is_repair_switch= 0x0004, // must be toggled to exit level
	_side_is_destructive_switch= 0x0008, // uses an item
	_side_is_lighted_switch= 0x0010, // switch must be lighted to use
	_side_switch_can_be_destroyed= 0x0020, // projectile hits toggle and destroy this switch
	_side_switch_can_only_be_hit_by_projectiles= 0x0040,

	_editor_dirty_bit= 0x4000 // used by the editor...
};

enum /* control panel side types */
{
	_panel_is_oxygen_refuel,
	_panel_is_shield_refuel,
	_panel_is_double_shield_refuel,
	_panel_is_triple_shield_refuel,
	_panel_is_light_switch, // light index in .permutation
	_panel_is_platform_switch, // platform index in .permutation
	_panel_is_tag_switch, // tag in .permutation (NONE is tagless)
	_panel_is_pattern_buffer,
	_panel_is_computer_terminal,
	NUMBER_OF_CONTROL_PANELS
};

#define SIDE_IS_CONTROL_PANEL(s) ((s)->flags & _side_is_control_panel)
#define SET_SIDE_CONTROL_PANEL(s, t) ((t) ? (s->flags |= (word) _side_is_control_panel) : (s->flags &= (word)~_side_is_control_panel))

#define GET_CONTROL_PANEL_STATUS(s) ((s)->flags & _control_panel_status)
#define SET_CONTROL_PANEL_STATUS(s, t) ((t) ? (s->flags |= (word) _control_panel_status) : (s->flags &= (word)~_control_panel_status))
#define TOGGLE_CONTROL_PANEL_STATUS(s) ((s)->flags ^= _control_panel_status)

#define SIDE_IS_REPAIR_SWITCH(s) ((s)->flags & _side_is_repair_switch)
#define SET_SIDE_IS_REPAIR_SWITCH(s, t) ((t) ? (s->flags |= (word) _side_is_repair_switch) : (s->flags &= (word)~_side_is_repair_switch))

/* Flags used by Vulcan */
#define SIDE_IS_DIRTY(s) ((s)->flags&_editor_dirty_bit)
#define SET_SIDE_IS_DIRTY(s, t) ((t)?(s->flags|=(word)_editor_dirty_bit):(s->flags&=(word)~_editor_dirty_bit))

enum /* side types (largely redundant; most of this could be guessed for examining adjacent polygons) */
{
	_full_side, /* primary texture is mapped floor-to-ceiling */
	_high_side, /* primary texture is mapped on a panel coming down from the ceiling (implies 2 adjacent polygons) */
	_low_side, /* primary texture is mapped on a panel coming up from the floor (implies 2 adjacent polygons) */
	_composite_side, /* primary texture is mapped floor-to-ceiling, secondary texture is mapped into it (i.e., control panel) */
	_split_side /* primary texture is mapped onto a panel coming down from the ceiling, secondary
		texture is mapped on a panel coming up from the floor */
};

struct side_texture_definition
{
	world_distance x0, y0;
	shape_descriptor texture;
};

struct side_exclusion_zone
{
	world_point2d e0, e1, e2, e3;
};

struct side_data /* 64 bytes */
{
	short type;
	word flags;
	
	struct side_texture_definition primary_texture;
	struct side_texture_definition secondary_texture;
	struct side_texture_definition transparent_texture; /* not drawn if .texture==NONE */

	/* all sides have the potential of being impassable; the exclusion zone is the area near
		the side which cannot be walked through */
	struct side_exclusion_zone exclusion_zone;

	short control_panel_type; /* Only valid if side->flags & _side_is_control_panel */
	short control_panel_permutation; /* platform index, light source index, etc... */
	
	short primary_transfer_mode; /* These should be in the side_texture_definition.. */
	short secondary_transfer_mode;
	short transparent_transfer_mode;

	short polygon_index, line_index;

	short primary_lightsource_index;	
	short secondary_lightsource_index;
	short transparent_lightsource_index;

	fixed ambient_delta;

	short unused[1];
};

#define POLYGON_TAG 'POLY'

#define MAXIMUM_VERTICES_PER_POLYGON 8

enum /* polygon types */
{
	_polygon_is_normal,
	_polygon_is_item_impassable,
	_polygon_is_monster_impassable,
	_polygon_is_hill, /* for king-of-the-hill */
	_polygon_is_base, /* for capture the flag, rugby, etc. (team in .permutation) */
	_polygon_is_platform, /* platform index in .permutation */
	_polygon_is_light_on_trigger, /* lightsource index in .permutation */
	_polygon_is_platform_on_trigger, /* polygon index in .permutation */
	_polygon_is_light_off_trigger, /* lightsource index in .permutation */
	_polygon_is_platform_off_trigger, /* polygon index in .permutation */
	_polygon_is_teleporter, /* .permutation is polygon_index of destination */
	_polygon_is_zone_border,
	_polygon_is_goal,
	_polygon_is_visible_monster_trigger,
	_polygon_is_invisible_monster_trigger,
	_polygon_is_dual_monster_trigger,
	_polygon_is_item_trigger, /* activates all items in this zone */
	_polygon_must_be_explored,
	_polygon_is_automatic_exit /* if success conditions are met, causes automatic transport too next level */
};

#define POLYGON_IS_DETACHED_BIT 0x4000
#define POLYGON_IS_DETACHED(p) ((p)->flags&POLYGON_IS_DETACHED_BIT)
#define SET_POLYGON_DETACHED_STATE(p, v) ((v)?((p)->flags|=POLYGON_IS_DETACHED_BIT):((p)->flags&=~POLYGON_IS_DETACHED_BIT))

struct polygon_data /* 128 bytes */
{
	short type;
	word flags;
	short permutation;

	short vertex_count;
	short endpoint_indexes[MAXIMUM_VERTICES_PER_POLYGON]; /* clockwise */
	short line_indexes[MAXIMUM_VERTICES_PER_POLYGON];
	
	shape_descriptor floor_texture, ceiling_texture;
	world_distance floor_height, ceiling_height;
	short floor_lightsource_index, ceiling_lightsource_index;
	
	long area; /* in world_distance^2 units */
	
	short first_object;
	
	/* precalculated impassability information; each polygon has a list of lines and points
		that anything big (i.e., monsters but not projectiles) inside it must check against when
		ending a move inside it. */
	short first_exclusion_zone_index;
	short line_exclusion_zone_count;
	short point_exclusion_zone_count;

	short floor_transfer_mode;
	short ceiling_transfer_mode;
	
	short adjacent_polygon_indexes[MAXIMUM_VERTICES_PER_POLYGON];
	
	/* a list of polygons within WORLD_ONE of us */
	short first_neighbor_index;
	short neighbor_count;
	
	world_point2d center;
	
	short side_indexes[MAXIMUM_VERTICES_PER_POLYGON];
	
	world_point2d floor_origin, ceiling_origin;
	
	short media_index;
	short media_lightsource_index;
	
	/* NONE terminated list of _saved_sound_source indexes which must be checked while a
		listener is inside this polygon (can be none) */
	short sound_source_indexes;
	
	// either can be NONE
	short ambient_sound_image_index;
	short random_sound_image_index;
	
	short unused[1];
};


#define LIGHTSOURCE_TAG 'LITE'
/* ---------- constants */

#define MAXIMUM_LIGHTS_PER_MAP 64

enum /* default light types */
{
	_normal_light,
	_strobe_light,
	_media_light,
	NUMBER_OF_LIGHT_TYPES
};

enum /* states */
{
	_light_becoming_active,
	_light_primary_active,
	_light_secondary_active,
	_light_becoming_inactive,
	_light_primary_inactive,
	_light_secondary_inactive
};

/* ---------- static light data */

enum /* lighting functions */
{
	_constant_lighting_function, // maintain final intensity for period
	_linear_lighting_function, // linear transition between initial and final intensity over period
	_smooth_lighting_function, // sine transition between initial and final intensity over period
	_flicker_lighting_function, // intensity in [smooth_intensity(t),final_intensity]
	NUMBER_OF_LIGHTING_FUNCTIONS
};

/* as intensities, transition functions are given the primary periods of the active and inactive
	state, plus the intensity at the time of transition */
struct lighting_function_specification /* 7*2 == 14 bytes */
{
	short function;
	
	short period, delta_period;
	fixed intensity, delta_intensity;
};

enum /* static flags */
{
	_light_is_initially_active,
	_light_has_slaved_intensities,
	_light_is_stateless,
	NUMBER_OF_STATIC_LIGHT_FLAGS /* <=16 */
};

#define LIGHT_IS_INITIALLY_ACTIVE(s) TEST_FLAG16((s)->flags, _light_is_initially_active)
#define LIGHT_IS_STATELESS(s) TEST_FLAG16((s)->flags, _light_is_stateless)

#define SET_LIGHT_IS_INITIALLY_ACTIVE(s, v) SET_FLAG16((s)->flags, _light_is_initially_active, (v))
#define SET_LIGHT_IS_STATELESS(s, v) SET_FLAG16((s)->flags, _light_is_stateless, (v))

struct static_light_data /* 8*2 + 6*14 == 100 bytes */
{
	short type;
	word flags;

	short phase; // initializer, so lights may start out-of-phase with each other
	
	struct lighting_function_specification primary_active, secondary_active, becoming_active;
	struct lighting_function_specification primary_inactive, secondary_inactive, becoming_inactive;
	
	short tag;
	
	short unused[4];
};

/* ---------- dynamic light data */

struct light_data /* 14*2 + 100 == 128 bytes */
{
	word flags;
	short state;
	
	// result of lighting function
	fixed intensity;
	
	// data recalculated each function changed; passed to lighting_function each update
	short phase, period;
	fixed initial_intensity, final_intensity;

	short unused[4];

	struct static_light_data static_data;
};


#define ANNOTATION_TAG 'NOTE'

#define MAXIMUM_ANNOTATIONS_PER_MAP 20
#define MAXIMUM_ANNOTATION_TEXT_LENGTH 64

struct map_annotation
{
	short type; /* turns into color, font, size, style, etc... */
	
	world_point2d location; /* where to draw this (lower left) */
	short polygon_index; /* only displayed if this polygon is in the automap */
	
	char text[MAXIMUM_ANNOTATION_TEXT_LENGTH];
};


#define OBJECT_TAG 'OBJS'

#define MAXIMUM_SAVED_OBJECTS 384

enum /* map object types */
{
	_saved_monster,	/* .index is monster type */
	_saved_object,	/* .index is scenery type */
	_saved_item,	/* .index is item type */
	_saved_player,	/* .index is team bitfield */
	_saved_goal,	/* .index is goal number */
	_saved_sound_source /* .index is source type, .facing is sound volume */
};

enum /* map object flags */
{
	_map_object_is_invisible= 0x0001, /* initially invisible */
	_map_object_is_platform_sound= 0x0001,
	_map_object_hanging_from_ceiling= 0x0002, /* used for calculating absolute .z coordinate */
	_map_object_is_blind= 0x0004, /* monster cannot activate by sight */
	_map_object_is_deaf= 0x0008, /* monster cannot activate by sound */
	_map_object_floats= 0x0010, /* used by sound sources caused by media */
	_map_object_is_network_only= 0x0020 /* for items only */
	
	// top four bits is activation bias for monsters
};

#define DECODE_ACTIVATION_BIAS(f) ((f)>>12)
#define ENCODE_ACTIVATION_BIAS(b) ((b)<<12)

struct map_object /* 16 bytes */
{
	short type; /* _saved_monster, _saved_object, _saved_item, ... */
	short index;
	short facing;
	short polygon_index;
	world_point3d location; // .z is a delta
	
	word flags;
};

#define MAP_INFO_TAG 'Minf'
struct static_data
{
	short environment_code;
	
	short physics_model;
	short song_index;
	short mission_flags;
	short environment_flags;
	
	short unused[4];

	char level_name[LEVEL_NAME_LENGTH];
	long entry_point_flags;
};

#define ITEM_PLACEMENT_STRUCTURE_TAG 'plac'
/* ---------- new object frequency structures. */

#define MAXIMUM_OBJECT_TYPES 64

enum // flags for object_frequency_definition
{
	_reappears_in_random_location= 0x0001
};

struct object_frequency_definition
{
	word flags;
	
	short initial_count;   // number that initially appear. can be greater than maximum_count
	short minimum_count;   // this number of objects will be maintained.
	short maximum_count;   // cant exceed this, except at the beginning of the level.
	
	short random_count;    // maximum random occurences of the object
	word random_chance;    // in (0, 65535]
};


#define PLATFORM_STATIC_DATA_TAG 'plat'

#define MAXIMUM_PLATFORMS_PER_MAP 64

enum /* platform types */
{
	_platform_is_spht_door,
	_platform_is_spht_split_door,
	_platform_is_locked_spht_door,
	_platform_is_spht_platform,
	_platform_is_noisy_spht_platform,
	_platform_is_heavy_spht_door,
	_platform_is_pfhor_door,
	_platform_is_heavy_spht_platform,
	_platform_is_pfhor_platform,
	
	NUMBER_OF_PLATFORM_TYPES
};

enum /* platform speeds */
{
	_very_slow_platform= WORLD_ONE/(4*TICKS_PER_SECOND),
	_slow_platform= WORLD_ONE/(2*TICKS_PER_SECOND),
	_fast_platform= 2*_slow_platform,
	_very_fast_platform= 3*_slow_platform,
	_blindingly_fast_platform= 4*_slow_platform
};

enum /* platform delays */
{
	_no_delay_platform= 0, /* use carefully; difficult to reincarnate on */
	_short_delay_platform= TICKS_PER_SECOND,
	_long_delay_platform= 2*TICKS_PER_SECOND,
	_very_long_delay_platform= 4*TICKS_PER_SECOND,
	_extremely_long_delay_platform= 8*TICKS_PER_SECOND
};

enum /* static platform flags */
{
	_platform_is_initially_active, /* otherwise inactive */
	_platform_is_initially_extended, /* high for floor platforms, low for ceiling platforms, closed for two-way platforms */
	_platform_deactivates_at_each_level, /* this platform will deactivate each time it reaches a discrete level */
	_platform_deactivates_at_initial_level, /* this platform will deactivate upon returning to its original position */
	_platform_activates_adjacent_platforms_when_deactivating, /* when deactivating, this platform activates adjacent platforms */
	_platform_extends_floor_to_ceiling, /* i.e., there is no empty space when the platform is fully extended */
	_platform_comes_from_floor, /* platform rises from floor */
	_platform_comes_from_ceiling, /* platform lowers from ceiling */
	_platform_causes_damage, /* when obstructed by monsters, this platform causes damage */
	_platform_does_not_activate_parent, /* does not reactive its parent (i.e., that platform which activated it) */
	_platform_activates_only_once, /* cannot be activated a second time */
	_platform_activates_light, /* activates floor and ceiling lightsources while activating */
	_platform_deactivates_light, /* deactivates floor and ceiling lightsources while deactivating */
	_platform_is_player_controllable, /* i.e., door: players can use action key to change the state and/or direction of this platform */
	_platform_is_monster_controllable, /* i.e., door: monsters can expect to be able to move this platform even if inactive */
	_platform_reverses_direction_when_obstructed,
	_platform_cannot_be_externally_deactivated, /* when active, can only be deactivated by itself */
	_platform_uses_native_polygon_heights, /* complicated interpretation; uses native polygon heights during automatic min,max calculation */
	_platform_delays_before_activation, /* whether or not the platform begins with the maximum delay before moving */
	_platform_activates_adjacent_platforms_when_activating,
	_platform_deactivates_adjacent_platforms_when_activating,
	_platform_deactivates_adjacent_platforms_when_deactivating,
	_platform_contracts_slower,
	_platform_activates_adjacent_platforms_at_each_level,
	_platform_is_locked,
	_platform_is_secret,
	_platform_is_door,
	NUMBER_OF_STATIC_PLATFORM_FLAGS /* <=32 */
};

struct static_platform_data
{
	short type;
	short speed, delay;
	world_distance maximum_height, minimum_height; /* if NONE then calculated in some reasonable way */

	unsigned long static_flags;
	
	short polygon_index;
	
	short tag;
	
	short unused[7];
};


#define ENDPOINT_DATA_TAG 'EPNT'

// Don't worry about this one, because Marathon2 will calculate it for you.

#define MEDIA_TAG 'medi'

#define MAXIMUM_MEDIAS_PER_MAP 16

enum /* media types */
{
	_media_water,
	_media_lava,
	_media_goo,
	_media_sewage,
	NUMBER_OF_MEDIA_TYPES
};

enum /* media flags */
{
	_media_sound_obstructed_by_floor, // this media makes no sound when under the floor
	
	NUMBER_OF_MEDIA_FLAGS /* <= 16 */
};

#define MEDIA_SOUND_OBSTRUCTED_BY_FLOOR(m) TEST_FLAG16((m)->flags, _media_sound_obstructed_by_floor)

#define SET_MEDIA_SOUND_OBSTRUCTED_BY_FLOOR(m, v) SET_FLAG16((m)->flags, _media_sound_obstructed_by_floor, (v))

enum /* media detonation types */
{
	_small_media_detonation_effect,
	_medium_media_detonation_effect,
	_large_media_detonation_effect,
	_large_media_emergence_effect,
	NUMBER_OF_MEDIA_DETONATION_TYPES
};

enum /* media sounds */
{
	_media_snd_feet_entering,
	_media_snd_feet_leaving,
	_media_snd_head_entering,
	_media_snd_head_leaving,
	_media_snd_splashing,
	_media_snd_ambient_over,
	_media_snd_ambient_under,
	_media_snd_platform_entering,
	_media_snd_platform_leaving,
	
	NUMBER_OF_MEDIA_SOUNDS
};

/* ---------- structures */

struct media_data /* 32 bytes */
{
	short type;
	word flags;

	/* this light is not used as a real light; instead, the intensity of this light is used to
		determine the height of the media: height= low + (high-low)*intensity ... this sounds
		gross, but it makes media heights as flexible as light intensities; clearly discontinuous
		light functions (e.g., strobes) should not be used */
	short light_index;

	/* this is the maximum external velocity due to current; acceleration is 1/32nd of this */
	angle current_direction;
	world_distance current_magnitude;
	
	world_distance low, high;
	
	world_point2d origin;
	world_distance height;

	fixed minimum_light_intensity;
	shape_descriptor texture;
	short transfer_mode;
	
	short unused[2];
};


#define AMBIENT_SOUND_TAG 'ambi'

#define MAXIMUM_AMBIENT_SOUND_IMAGES_PER_MAP 64

// non-directional ambient component
struct ambient_sound_image_data // 16 bytes
{
	word flags;
	
	short sound_index;
	short volume;

	short unused[5];
};

#define RANDOM_SOUND_TAG 'bonk'

#define MAXIMUM_RANDOM_SOUND_IMAGES_PER_MAP 64

enum // sound image flags
{
	_sound_image_is_non_directional= 0x0001 // ignore direction
};

// possibly directional random sound effects
struct random_sound_image_data // 32 bytes
{
	word flags;
	
	short sound_index;
	
	short volume, delta_volume;
	short period, delta_period;
	angle direction, delta_direction;
	fixed pitch, delta_pitch;
	
	// only used at run-time; initialize to NONE
	short phase;
	
	short unused[3];
};

#define TERMINAL_DATA_TAG 'term'
/*
	Preprocessed format:
	static:
		long total_length;
		short grouping_count;
		short font_changes_count;
		short total_text_length;
	dynamic:
		struct terminal_groupings groups[grouping_count];
		struct text_face_data[font_changes_count];
		char text;
*/

/* ------------ structures */
struct static_preprocessed_terminal_data {
	short total_length;
	short flags;
	short lines_per_page; /* Added for internationalization/sync problems */
	short grouping_count;
	short font_changes_count;
};

struct terminal_groupings {
	short flags; /* varies.. */
	short type; /* _information_text, _checkpoint_text, _briefing_text, _movie, _sound_bite, _soundtrack */
	short permutation; /* checkpoint id for chkpt, level id for _briefing, movie id for movie, sound id for sound, soundtrack id for soundtrack */
	short start_index;
	short length;
	short maximum_line_count;
};

struct text_face_data {
	short index;
	short face;
	short color;
};

/* ----------------------------- Physics model tags ----------------- */
#define MONSTER_PHYSICS_TAG 'MNpx'

/*
MONSTER_DEFINITIONS.H
Monday, May 30, 1994 9:04:01 PM
*/

/* ---------- macros */

#define TYPE_IS_NEUTRAL(definition,type) (!((definition->friends|definition->enemies)&monster_definitions[type].class))
#define TYPE_IS_ENEMY(definition,type) (definition->enemies&monster_definitions[type].class)
#define TYPE_IS_FRIEND(definition,type) (definition->friends&monster_definitions[type].class)

/* ---------- constants */

enum /* monster classes */
{
	_class_player_bit,
	_class_human_civilian_bit,
	_class_madd_bit,
	_class_possessed_hummer_bit,
		
	_class_defender_bit,

	_class_fighter_bit,
	_class_trooper_bit,
	_class_hunter_bit,
	_class_enforcer_bit,
	_class_juggernaut_bit,
	_class_hummer_bit,
	
	_class_compiler_bit,
	_class_cyborg_bit,
	_class_assimilated_civilian_bit,

	_class_tick_bit,
	_class_yeti_bit,

	_class_player= 1<<_class_player_bit,
	_class_human_civilian= 1<<_class_human_civilian_bit,
	_class_madd= 1<<_class_madd_bit,
	_class_possessed_hummer= 1<<_class_possessed_hummer_bit,
	_class_human= _class_player|_class_human_civilian|_class_madd|_class_possessed_hummer,
	
	_class_defender= 1<<_class_defender_bit,

	_class_fighter= 1<<_class_fighter_bit,
	_class_trooper= 1<<_class_trooper_bit,
	_class_hunter= 1<<_class_hunter_bit,
	_class_enforcer= 1<<_class_enforcer_bit,
	_class_juggernaut= 1<<_class_juggernaut_bit,
	_class_pfhor= _class_fighter|_class_trooper|_class_hunter|_class_enforcer|_class_juggernaut,
	
	_class_compiler= 1<<_class_compiler_bit,
	_class_cyborg= 1<<_class_cyborg_bit,
	_class_assimilated_civilian= 1<<_class_assimilated_civilian_bit,
	_class_hummer= 1<<_class_hummer_bit,
	_class_client= _class_compiler|_class_assimilated_civilian|_class_cyborg|_class_hummer,
	
	_class_tick= 1<<_class_tick_bit,
	_class_yeti= 1<<_class_yeti_bit,
	_class_native= _class_tick|_class_yeti,

	_class_hostile_alien= _class_pfhor|_class_client,
	_class_neutral_alien= _class_native
};

enum /* intelligence: maximum polygon switches before losing lock */
{
	_intelligence_low= 2,
	_intelligence_average= 3,
	_intelligence_high= 8
};

enum /* door retry masks */
{
	_slow_door_retry_mask= 63,
	_normal_door_retry_mask= 31,
	_fast_door_retry_mask= 15,
	_vidmaster_door_retry_mask= 3
};

enum /* flags */
{
	_monster_is_omniscent= 0x1, /* ignores line-of-sight during find_closest_appropriate_target() */
	_monster_flys= 0x2,
	_monster_is_alien= 0x4, /* moves slower on slower levels, etc. */
	_monster_major= 0x8, /* type -1 is minor */
	_monster_minor= 0x10, /* type +1 is major */
	_monster_cannot_be_dropped= 0x20, /* low levels cannot skip this monster */
	_monster_floats= 0x40, /* exclusive from flys; forces the monster to take +h gradually */
	_monster_cannot_attack= 0x80, /* monster has no weapons and cannot attack (runs constantly to safety) */
	_monster_uses_sniper_ledges= 0x100, /* sit on ledges and hurl shit at the player (ranged attack monsters only) */
	_monster_is_invisible= 0x200, /* this monster uses _xfer_invisibility */
	_monster_is_subtly_invisible= 0x400, /* this monster uses _xfer_subtle_invisibility */
	_monster_is_kamakazi= 0x800, /* monster does shrapnel damage and will suicide if close enough to target */
	_monster_is_berserker= 0x1000, /* below 1/4 vitality this monster goes berserk */
	_monster_is_enlarged= 0x2000, /* monster is 1.25 times normal height */
	_monster_has_delayed_hard_death= 0x4000, /* always dies soft, then switches to hard */
	_monster_fires_symmetrically= 0x8000, /* fires at dy, simultaneously */
	_monster_has_nuclear_hard_death= 0x10000, /* players screen whites out and slowly recovers */
	_monster_cant_fire_backwards= 0x20000, /* monster cant turn more than 135 to fire */
	_monster_can_die_in_flames= 0x40000, /* uses humanoid flaming body shape */
	_monster_waits_with_clear_shot= 0x80000, /* will sit and fire (slowly) if we have a clear shot */
	_monster_is_tiny= 0x100000, /* 0.25-size normal height */
	_monster_attacks_immediately= 0x200000, /* monster will try an attack immediately */
	_monster_is_not_afraid_of_water= 0x400000,
	_monster_is_not_afraid_of_sewage= 0x800000,
	_monster_is_not_afraid_of_lava= 0x1000000,
	_monster_is_not_afraid_of_goo= 0x2000000,
	_monster_can_teleport_under_media= 0x4000000,
	_monster_chooses_weapons_randomly= 0x8000000
	/* monsters unable to open doors have door retry masks of NONE */
	/* monsters unable to switch levels have min,max ledge deltas of 0 */
	/* monsters unstopped by bullets have hit frames of NONE */
};

enum /* monster speeds (world_distance per tick); also used for projectiles */
{
	_speed_slow= WORLD_ONE/120,
	_speed_medium= WORLD_ONE/80,
	_speed_almost_fast= WORLD_ONE/70,
	_speed_fast= WORLD_ONE/40,
	_speed_superfast1= WORLD_ONE/30,
	_speed_superfast2= WORLD_ONE/28,
	_speed_superfast3= WORLD_ONE/26,
	_speed_superfast4= WORLD_ONE/24,
	_speed_superfast5= WORLD_ONE/22,
	_speed_blinding= WORLD_ONE/20,
	_speed_insane= WORLD_ONE/10
};

#define NORMAL_MONSTER_GRAVITY (WORLD_ONE/120)
#define NORMAL_MONSTER_TERMINAL_VELOCITY (WORLD_ONE/14)

/* ---------- monster definition structures */

struct attack_definition
{
	short type;
	short repetitions;
	angle error; /* error is added to the firing angle */
	world_distance range; /* beyond which we cannot attack */
	short attack_shape; /* attack occurs when keyframe is displayed */
	
	world_distance dx, dy, dz; /* +dy is right, +dx is out, +dz is up */
};

struct monster_definition /* <128 bytes */
{
	short collection;
	
	short vitality;
	unsigned long immunities, weaknesses;
	unsigned long flags;

	long class; /* our class */
	long friends, enemies; /* bit fields of what classes we consider friendly and what types we dont like */

	fixed sound_pitch;
	short activation_sound, friendly_activation_sound, clear_sound;
	short kill_sound, apology_sound, friendly_fire_sound;
	short flaming_sound; /* the scream we play when we go down in flames */
	short random_sound, random_sound_mask; /* if moving and locked play this sound if we get time and our mask comes up */

	short carrying_item_type; /* an item type we might drop if we dont explode */

	world_distance radius, height;
	world_distance preferred_hover_height;
	world_distance minimum_ledge_delta, maximum_ledge_delta;
	fixed external_velocity_scale;
	short impact_effect, melee_impact_effect, contrail_effect;

	short half_visual_arc, half_vertical_visual_arc;
	world_distance visual_range, dark_visual_range;
	short intelligence;
	short speed, gravity, terminal_velocity;
	short door_retry_mask;
	short shrapnel_radius; /* no shrapnel if NONE */
	struct damage_definition shrapnel_damage;

	shape_descriptor hit_shapes;
	shape_descriptor hard_dying_shape, soft_dying_shape; /* minus dead frame */
	shape_descriptor hard_dead_shapes, soft_dead_shapes; /* NONE for vanishing */
	shape_descriptor stationary_shape, moving_shape;
	shape_descriptor teleport_in_shape, teleport_out_shape;
	
	/* which type of attack the monster actually uses is determined at attack time; typically
		melee attacks will occur twice as often as ranged attacks because the monster will be
		stopped (and stationary monsters attack twice as often as moving ones) */
	short attack_frequency;
	struct attack_definition melee_attack;
	struct attack_definition ranged_attack;
};

#define EFFECTS_PHYSICS_TAG 'FXpx'

enum /* flags */
{
	_end_when_animation_loops= 0x0001,
	_end_when_transfer_animation_loops= 0x0002,
	_sound_only= 0x0004, /* play the animations initial sound and nothing else */
	_make_twin_visible= 0x0008,
	_media_effect= 0x0010
};

/* ---------- structures */

struct effect_definition
{
	short collection, shape;

	fixed sound_pitch;
	
	word flags;
	short delay, delay_sound;
};


#define PROJECTILE_PHYSICS_TAG 'PRpx'


#define PHYSICS_PHYSICS_TAG 'PXpx'
/* ---------- constants */

enum /* projectile flags */
{
	_guided= 0x0001,
	_stop_when_animation_loops= 0x0002,
	_persistent= 0x0004, /* does stops doing damage and stops moving against a target, but doesn't vanish */
	_alien_projectile= 0x0008, /* does less damage and moves slower on lower levels */
	_affected_by_gravity= 0x0010,
	_no_horizontal_error= 0x0020,
	_no_vertical_error= 0x0040,
	_can_toggle_control_panels= 0x0080,
	_positive_vertical_error= 0x0100,
	_melee_projectile= 0x0200, /* can use a monsters custom melee detonation */
	_persistent_and_virulent= 0x0400, /* keeps moving and doing damage after a successful hit */
	_usually_pass_transparent_side= 0x0800,
	_sometimes_pass_transparent_side= 0x1000,
	_doubly_affected_by_gravity= 0x2000,
	_rebounds_from_floor= 0x4000, /* unless v.z<kvzMIN */
	_penetrates_media= 0x8000, /* huh uh huh ... i said penetrate */
	_becomes_item_on_detonation= 0x10000, /* item type in .permutation field of projectile */
	_bleeding_projectile= 0x20000, /* can use a monsters custom bleeding detonation */
	_horizontal_wander= 0x40000, /* random horizontal error perpendicular to direction of movement */
	_vertical_wander= 0x80000, /* random vertical movement perpendicular to direction of movement */
	_affected_by_half_gravity= 0x100000
};

/* ---------- structures */

struct projectile_definition
{
	short collection, shape; /* collection can be NONE (invisible) */
	short detonation_effect, media_detonation_effect;
	short contrail_effect, ticks_between_contrails, maximum_contrails; /* maximum of NONE is infinite */
	short media_projectile_promotion;

	world_distance radius; /* can be zero and will still hit */
	world_distance area_of_effect; /* one target if ==0 */
	struct damage_definition damage;

	unsigned long flags;

	world_distance speed;
	world_distance maximum_range;

	fixed sound_pitch;	
	short flyby_sound, rebound_sound;
};


#define WEAPONS_PHYSICS_TAG 'WPpx'
enum /* weapon classes */
{
	_melee_class, /* normal weapon, no ammunition, both triggers do the same thing */
	_normal_class, /* normal weapon, one ammunition type, both triggers do the same thing */
	_dual_function_class, /* normal weapon, one ammunition type, trigger does something different */
	_twofisted_pistol_class, /* two can be held at once (differnet triggers), same ammunition */
	_multipurpose_class /* two weapons in one (assault rifle, grenade launcher), two different
		ammunition types with two separate triggers; secondary ammunition is discrete (i.e., it
		is never loaded explicitly but appears in the weapon) */
};

enum /* weapon flags */
{
	_no_flags= 0x0,
	_weapon_is_automatic= 0x01,
	_weapon_disappears_after_use= 0x02,
	_weapon_plays_instant_shell_casing_sound= 0x04,
	_weapon_overloads= 0x08,
	_weapon_has_random_ammo_on_pickup= 0x10,
	_powerup_is_temporary= 0x20,
	_weapon_reloads_in_one_hand= 0x40,
	_weapon_fires_out_of_phase= 0x80,
	_weapon_fires_under_media= 0x100,
	_weapon_triggers_share_ammo= 0x200,
	_weapon_secondary_has_angular_flipping= 0x400
};

enum {
	_weapon_in_hand_collection= 1,
	_fist_idle= 0,
	_fist_punching,
	_pistol_idle,
	_pistol_firing,
	_pistol_reloading,
	_shotgun_idle,
	_shotgun_firing,
	_shotgun_reloading,
	_assault_rifle_idle,
	_assault_rifle_firing,
	_assault_rifle_reloading,
	_fusion_idle,
	_fusion_firing,
	_missile_launcher_idle,
	_missile_launcher_firing,
	_flamethrower_idle,
	_flamethrower_transit,
	_flamethrower_firing,
	_assault_rifle_shell_casing,
	_pistol_shell_casing,
	_fusion_charged,
	_alien_weapon_idle,
	_alien_weapon_firing
};

/* ---------- shell casings */

enum // shell casing types
{
	_shell_casing_assault_rifle,
	_shell_casing_pistol,
	_shell_casing_pistol_left,
	_shell_casing_pistol_right,
	
	NUMBER_OF_SHELL_CASING_TYPES
};

struct shell_casing_definition
{
	short collection, shape;
	
	fixed x0, y0;
	fixed vx0, vy0;
	fixed dvx, dvy;
};

struct trigger_definition {
	short rounds_per_magazine;
	short ammunition_type;
	short ticks_per_round;
	short recovery_ticks;
	short charging_ticks;
	world_distance recoil_magnitude;
	short firing_sound;
	short click_sound;
	short charging_sound;
	short shell_casing_sound;
	short reloading_sound;
	short charged_sound;
	short projectile_type;
	short theta_error;
	short dx, dz;
	short shell_casing_type;
	short burst_count;
};

struct weapon_definition {
	short item_type;
	short powerup_type;
	short weapon_class;
	short flags;

	fixed firing_light_intensity;
	short firing_intensity_decay_ticks;

	/* weapon will come up to FIXED_ONE when fired; idle_heightbob_amplitude should be in
		the range [0,FIXED_ONE] */
	fixed idle_height, bob_amplitude, kick_height, reload_height;
	fixed idle_width, horizontal_amplitude;

	/* each weapon has three basic animations: idle, firing and reloading.  sounds and frames
		are pulled from the shape collection.  for automatic weapons the firing animation loops
		until the trigger is released or the gun is empty and the gun begins rising as soon as
		the trigger is depressed and is not lowered until the firing animation stops.  for single
		shot weapons the animation loops once; the weapon is raised and lowered as soon as the
		firing animation terminates */
	short collection;
	short idle_shape, firing_shape, reloading_shape;
	short unused;
	short charging_shape, charged_shape;

	/* How long does it take to ready the weapon? */
	/* load_rounds_tick is the point which you actually load them. */
	short ready_ticks, await_reload_ticks, loading_ticks, finish_loading_ticks, powerup_ticks;

	struct trigger_definition weapons_by_trigger[NUMBER_OF_TRIGGERS];
};

/* ----------------------------- Save/Load game tags ----------------- */
/* These tags are only present in a save game, and won't be detailed in this document...... */
#define PLAYER_STRUCTURE_TAG 'plyr'
#define DYNAMIC_STRUCTURE_TAG 'dwol'
#define OBJECT_STRUCTURE_TAG 'mobj'
#define MAP_INDEXES_TAG 'iidx'
#define AUTOMAP_LINES 'alin'
#define AUTOMAP_POLYGONS 'apol'
#define MONSTERS_STRUCTURE_TAG 'mOns'
#define EFFECTS_STRUCTURE_TAG 'fx  '
#define PROJECTILES_STRUCTURE_TAG 'bang'
#define PLATFORM_STRUCTURE_TAG 'PLAT'
#define WEAPON_STATE_TAG 'weap'
#define TERMINAL_STATE_TAG 'cint'
